from obs import ObsClient
import time
import hashlib
from io import BytesIO
from typing import Optional
import logging

class OBSUploader:
    """华为云OBS上传器"""
    
    def __init__(self, config: dict):
        obs_config = config['obs']
        # 确保 endpoint 包含完整的 https:// 前缀
        server = obs_config['endpoint']
        if not server.startswith(('http://', 'https://')):
            server = f"https://{server}"
            
        print(f"初始化OBS客户端: endpoint={server}, bucket={obs_config['bucket']}")
            
        self.client = ObsClient(
            access_key_id=obs_config['access_key'],
            secret_access_key=obs_config['secret_key'],
            server=server,
            path_style=True  # 改回使用路径风格
        )
        self.bucket = obs_config['bucket']
        self.retry_times = obs_config['retry_times']
        self.url_expire_seconds = obs_config['url_expire_seconds']
        self.endpoint = server
        self.manual_name = config.get('manual_name', 'default')  # 获取操作手册名称
        
        # 测试bucket是否可访问
        try:
            resp = self.client.headBucket(self.bucket)
            print(f"测试访问bucket结果: status={resp.status}")
        except Exception as e:
            print(f"测试访问bucket失败: {str(e)}")
        
    def upload(self, image: dict) -> Optional[str]:
        """上传图片到OBS"""
        try:
            # 生成唯一的文件名
            file_name = self._generate_file_name(image)
            print(f"准备上传文件: {file_name}")
            
            # 使用二进制数据流上传
            content = BytesIO(image['image_data'])
            
            # 重试机制
            for attempt in range(self.retry_times):
                try:
                    print(f"第{attempt+1}次尝试上传...")
                    # 上传图片，设置对象ACL为公共读
                    resp = self.client.putObject(
                        bucketName=self.bucket,
                        objectKey=file_name,
                        content=content,
                        metadata={'Content-Type': f'image/{image["format"].lower()}'},
                        headers={'acl':'public-read'}  # 设置对象ACL为公共读
                    )
                    
                    print(f"上传响应: status={resp.status}, requestId={resp.requestId}")
                    
                    if resp.status < 300:  # 上传成功
                        # 生成普通URL而不是签名URL，因为对象已经是公共读
                        url = f"https://{self.bucket}.{self.endpoint.replace('https://', '')}/{file_name}"
                        print(f"上传成功，访问URL: {url}")
                        return url
                    else:
                        print(f"上传失败: status={resp.status}, reason={resp.reason}")
                        if hasattr(resp, 'errorCode'):
                            print(f"错误代码: {resp.errorCode}, 错误信息: {resp.errorMessage}")
                        
                except Exception as e:
                    print(f"第{attempt+1}次上传出错: {str(e)}")
                    time.sleep(1)
                    content.seek(0)
                    continue
                    
            return None
            
        except Exception as e:
            print(f"上传图片失败: {str(e)}")
            return None
        
    def _generate_file_name(self, image: dict) -> str:
        """生成唯一的文件名，使用操作手册名称作为前缀"""
        timestamp = int(time.time())
        content_hash = hashlib.md5(image['image_data']).hexdigest()[:8]
        return f"{self.manual_name}/images/{timestamp}_{content_hash}.{image['format']}"
        
    def _generate_url(self, file_name: str) -> str:
        """生成图片访问URL"""
        # 由于对象已设置为公共读，直接返回普通URL
        return f"https://{self.bucket}.{self.endpoint.replace('https://', '')}/{file_name}"
            
    def __del__(self):
        """确保关闭OBS客户端"""
        if hasattr(self, 'client'):
            self.client.close() 